#if !defined( __CINT__) || defined (__MAKECINT__)

#ifndef ROOT_TGDockableFrame
#include "TGDockableFrame.h"
#endif
#ifndef ROOT_TGMenu
#include "TGMenu.h"
#endif
#ifndef ROOT_TGMdiDecorFrame
#include "TGMdiDecorFrame.h"
#endif
#ifndef ROOT_TG3DLine
#include "TG3DLine.h"
#endif
#ifndef ROOT_TGuiBldHintsButton
#include "TGuiBldHintsButton.h"
#endif
#ifndef ROOT_TGMdiMenu
#include "TGMdiMenu.h"
#endif
#ifndef ROOT_TGColorDialog
#include "TGColorDialog.h"
#endif
#ifndef ROOT_TGListBox
#include "TGListBox.h"
#endif
#ifndef ROOT_TGNumberEntry
#include "TGNumberEntry.h"
#endif
#ifndef ROOT_TGuiBldHintsEditor
#include "TGuiBldHintsEditor.h"
#endif
#ifndef ROOT_TGFileDialog
#include "TGFileDialog.h"
#endif
#ifndef ROOT_TGShutter
#include "TGShutter.h"
#endif
#ifndef ROOT_TGButtonGroup
#include "TGButtonGroup.h"
#endif
#ifndef ROOT_TGFSContainer
#include "TGFSContainer.h"
#endif
#ifndef ROOT_TGuiBldEditor
#include "TGuiBldEditor.h"
#endif
#ifndef ROOT_TGFSComboBox
#include "TGFSComboBox.h"
#endif
#ifndef ROOT_TGView
#include "TGView.h"
#endif
#ifndef ROOT_TGMsgBox
#include "TGMsgBox.h"
#endif
#ifndef ROOT_TRootGuiBuilder
#include "TRootGuiBuilder.h"
#endif
#ifndef ROOT_TGStatusBar
#include "TGStatusBar.h"
#endif
#ifndef ROOT_TGToolTip
#include "TGToolTip.h"
#endif
#ifndef ROOT_TGToolBar
#include "TGToolBar.h"
#endif

#include "Riostream.h"

#endif

#include <fstream>
#include <istream>
#include <ostream>
#include <iterator>
#include <sstream>
#include <math.h>

#include "TomsonScattering.h"
#include "AppGUI.h"
#include "ArchiveWrapper.h"
#include "Exceptions.h"

void AppGUI::ReadConfig(std::map<std::string, std::vector<double> > &params, std::string &arcName)
{
    std::ifstream config("tomson_config.txt", std::ios::in);
    if( !config.is_open() )
    {
        // config file does not exist, create default config
        std::ofstream defaultConfig("tomson_config.txt", std::ios::out);
        if( !defaultConfig.is_open() )
        {
            std::cout << "failed to create default configuration file. Exiting" << std::endl;
            throw;
        }

        defaultConfig << "./microinstability_29068.old.root" << std::endl;

        defaultConfig << "ADT bit count " << 8 << std::endl;

        defaultConfig << "Chanel number " << 12 << std::endl;
        
        double deltal[5] = {7.73, 4.41, 4.41, 3.81, 3.81}; //polushirina of chanels
        defaultConfig << "delta ";
        for( int i = 0; i < 5; ++i )
            defaultConfig << deltal[i] << " ";
        defaultConfig << std::endl;

        defaultConfig << "Signal start time " << 0.2 << "\nSignal end time " << 1.0 << std::endl;
        defaultConfig << "Last signal start time " << 0.6 << "\nLast signal start time " << 1.6 << std::endl;
        
        defaultConfig << "Before maximum interval start " << 45 << "\nBefore maximum interval end " << 10 << std::endl;
        defaultConfig << "After maximum interval start " << 15 << "\nAfter maximum interval end " << 20 << std::endl;

        defaultConfig << "Peak calibration chanel " << 4 << std::endl;
        defaultConfig << "Second peak start time " << 1 << "\nSecond peak end time " << 1.4 << std::endl;

        defaultConfig << "Before second peak interval start " << 30 << "\nBefore second peak interval end " << 10 << std::endl;
        defaultConfig << "After second peak interval start " << 15 << "\nAfter second peak interval end " << 20 << std::endl;

        defaultConfig << "Time sync first peak offset " << -0.2 << "\nTime sync second peak offset " << 0.2 << std::endl;

        defaultConfig.close();

        config.open("tomson_config.txt", std::ios::in);
        if( !config.is_open() )
            return;
    }

    std::string str;
    config >> arcName;

    while( std::getline(config, str, '\n') )
    {
        size_t pos = str.find_first_of("0123456789");
        if( pos == std::string::npos )
            params[str.substr(0, str.length()-1)] = std::vector<double>(1, 0.0);
        else
        {
            std::string vals = str.substr(pos, str.length()-1);

            std::vector<double> converted;
            std::stringstream strStream(vals);
            double val = 0;
            while( strStream >> val )
            {
                converted.push_back(val);
            }

            params[str.substr(0, pos-1)] = converted;
        }
    }
    config.close(); 
}
void AppGUI::RunCalculate()
{
    m_scatteringMethod.SetChanelStates(m_configTab.GetChanelStates()); // on/off

    ArchiveWrapper arc(m_measTab.GetArcName(), m_measTab.GetShotNum());
    if( arc.Valid() )
    {
        m_visWrapper->Configure(m_configTab.DrawOscilograms(), m_configTab.PrintIntegrals());
        m_scatteringMethod.SetArchive(&arc);
        m_scatteringMethod.SetVisHelper(m_visWrapper);
        m_calibTab.GetCalib( m_calibData );

        try
        {
            // relative calibration using second peak must be done right on the spot
            // this is the perfect place to do it
            if( m_configTab.GetCalibOpts(peak_calib) == peak_normSecond )
            {
                m_scatteringMethod.CalculatePeakRatioCalib(m_calibData.peakRatioCalib, peak_normSecond, m_configTab.UseFilters());
                m_calibTab.ShowCalibrations(m_calibData);
            }

            if( !m_configTab.UseBgCalib() )
                m_calibData.bgCalib.assign(m_calibData.bgCalib.size(), ValueWithError(0,0));
            
            m_scatteringMethod.Calculate(m_calibData, m_configTab.UseStatWeight(), m_configTab.UseFilters(),
                    m_configTab.UseTimeSync(), m_configTab.AutoChooseChanels());
        }
        catch( ProcessingException const& e )
        {
            m_measTab.SetWarningText( std::string(e.what()) );
            std::cout << "Error: " << e.what() << std::endl;
            std::cerr << e.what() << std::endl;
            m_measTab.ClearData();
            return;
        }

        m_measTab.PrintData(m_scatteringMethod.GetTe(), m_scatteringMethod.GetNe());
        m_measTab.DrawData(m_scatteringMethod.GetLine(), m_scatteringMethod.GetPoints());
        if( m_configTab.ShowRawResults() )
        {
            m_measTab.PrintData(m_scatteringMethod.GetTe(false), m_scatteringMethod.GetNe(false), false);
            m_measTab.DrawData(m_scatteringMethod.GetLine(false), m_scatteringMethod.GetPoints(false), false);
        }
    }
}

void AppGUI::ParseShot(std::string &shotStr, int &shotNum, std::vector<bool> &chanels)
{
    chanels.assign(chanels.size(), false);
    std::string digits("1234567890");

    size_t start = shotStr.find_first_of(digits);
    size_t end = 0;
    if( start != std::string::npos )
    {
        end = shotStr.find_first_not_of(digits, start);
        if(end == std::string::npos)
                end = shotStr.length();

        std::istringstream( shotStr.substr(start, end-start) ) >> shotNum;
        
        int chanelNum = 0;
        bool modified = false;
        while( ( start = shotStr.find_first_of(digits, end) ) != std::string::npos )
        {
            end = shotStr.find_first_not_of(digits, start);
            if(end == std::string::npos)
                end = shotStr.length();

            if( std::istringstream( shotStr.substr(start, end-start) ) >> chanelNum )
                if( chanelNum <= chanels.size() && chanelNum > 0 )
                {
                    chanels[chanelNum-1] = true;
                    modified = true;
                }

            start = end+1;
        }

        if( !modified ) // if no valid chanel numbers were specified
            chanels.assign(chanels.size(), true);
    }
    
}

void AppGUI::InvokeReadCalibrations()
{
    m_calibTab.ReadCalibrations( m_measTab.GetShotNum() );
}

void AppGUI::ShowArcInfo()
{
    ArchiveWrapper arc(m_measTab.GetArcName(), -1);

    char name[4096];
    char path[4096];
    char first[4096];
    char last[4096];

    if( !arc.Valid() )
    {
        sprintf(name, "Archive name: Error");
        sprintf(path, "Archive full path: Invalid path");
        sprintf(first, "First shot number: Error");
        sprintf(last, "Last shot number: Error");
    }
    else
    {
        sprintf(name, "Archive name: Error");
        sprintf(path, "Archive full path: %s", m_measTab.GetArcName().c_str());
        sprintf(first, "First shot number: %d", arc.FirstShot());
        sprintf(last, "Last shot number: %d", arc.LastShot());
    }

    TGTransientFrame *fDialog = new TGTransientFrame(NULL, m_mainCompositeFrame, 700, 100);
    TGCompositeFrame *fFrame = new TGVerticalFrame(fDialog, 60, 20, kFixedWidth);

    fFrame->MoveResize(0,0,700,100);
    fFrame->SetLayoutManager(new TGVerticalLayout(fFrame));
    fFrame->SetLayoutBroken(kTRUE);

    TGLabel *arcPath = new TGLabel(fFrame, path);
    fFrame->AddFrame(arcPath, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));
    arcPath->MoveResize(16, 16, 700, 16);
    arcPath->SetTextJustify(kTextLeft);

    TGLabel *firstShot = new TGLabel(fFrame, first);
    fFrame->AddFrame(firstShot, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));
    firstShot->MoveResize(16, 48, 400, 16);
    firstShot->SetTextJustify(kTextLeft);

    TGLabel *lastShot = new TGLabel(fFrame, last);
    fFrame->AddFrame(lastShot, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));
    lastShot->MoveResize(16, 80, 400, 16);
    lastShot->SetTextJustify(kTextLeft);

    fDialog->AddFrame(fFrame, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));

    fDialog->MapSubwindows();
    fDialog->MapWindow();
}

void AppGUI::SaveCalibs()
{
    ArchiveWrapper arc( m_measTab.GetArcName(), -1, true );

    if( arc.Valid() )
    {
        m_calibTab.GetCalib(m_calibData);

        try
        {
            arc.SaveCalibrations(m_calibData);
        }
        catch( CalibrationException const & e )
        {
            std::cout << "Error: " << e.what() << std::endl;
            std::cerr << e.what() << std::endl;
            m_measTab.SetWarningText(e.what());
        }

    }
}

void AppGUI::RunCalibrateBg()
{
    std::vector<ValueWithError> bgCalib(m_calibData.bgCalib.size(), ValueWithError(0,0));
    m_calibTab.GetCalib(m_calibData);
    RunCalibrate(m_measTab.GetArcName(), bg_calib, bgCalib);
    m_calibData.bgCalib.assign(bgCalib.begin(), bgCalib.end());
    m_calibTab.ShowCalibrations(m_calibData);
}

void AppGUI::RunCalibratePeak()
{
    std::vector<ValueWithError> peakCalib(m_calibData.peakRatioCalib.size(), ValueWithError(0,0));
    m_calibTab.GetCalib(m_calibData);
    RunCalibrate(m_configTab.GetArcName(), peak_calib, peakCalib);

    // we nned to renorm calibrations to the chosen chanel us we used the sum nirmalization
    // if this is not done the calib coefficients are too small (much smaller, than if just normalized to selected chanel)
    calib_options opt = m_configTab.GetCalibOpts(peak_calib);
    if(  opt == peak_normSum )
        m_scatteringMethod.NormPeakRatioCalib(peakCalib, peak_normCustomCh);

    m_calibData.peakRatioCalib.assign(peakCalib.begin(), peakCalib.end());
    m_calibTab.ShowCalibrations(m_calibData);
}

void AppGUI::RunCalibrate(std::string arcName, calib_type type, std::vector<ValueWithError> &calib)
{
    std::vector<std::string> shots = m_configTab.GetShots(type);
    std::vector<bool> chanels(calib.size(), true);
    std::vector<int> timesChanelUsed(chanels.size(), 0);

    int shot = 0;
    for( int i = 0; i < shots.size(); ++i )
    {
        ParseShot(shots[i], shot, chanels);

        for( int j = 0; j < chanels.size(); ++j )
        {
            if( chanels[j] == true )
                ++timesChanelUsed[j];
        }
        
        ArchiveWrapper arc(arcName, shot);
        if( arc.Valid() )
        {
            std::vector<ValueWithError> tmpCalib;
            m_scatteringMethod.SetChanelStates(chanels);
            m_scatteringMethod.SetVisHelper(NULL);
            m_scatteringMethod.SetArchive(&arc);

            if(type == bg_calib)
                m_scatteringMethod.CalculateBgCalib(tmpCalib, m_configTab.UseFilters(), m_configTab.UseTimeSync());
            else if( type == peak_calib )
                m_scatteringMethod.CalculatePeakRatioCalib(tmpCalib, m_configTab.GetCalibOpts(peak_calib), m_configTab.UseFilters());
            else
                return;

            // calc stqandard deviation (error) in a single pass:
            for( int j = 0; j < calib.size(); ++j )
            {
                if( chanels[j] == true )
                {
                    calib[j].val += tmpCalib[j].val;
                    calib[j].error += tmpCalib[j].val*tmpCalib[j].val;
                }
            }
        }
    }

    for(int i = 0; i < calib.size(); ++i)
    {
        if( chanels[i] == true )
        {
            if( timesChanelUsed[i] > 1 )
            {
                calib[i].val /= timesChanelUsed[i];

                // calc dispersion sigma^2
                calib[i].error = calib[i].error/timesChanelUsed[i] - calib[i].val*calib[i].val;

                // calc variance s^2 = sigma^2 * n/(n-1) same as pow( error/(size*(size-1)), 0.5 );
                calib[i].error = pow( calib[i].error * timesChanelUsed[i] / ( timesChanelUsed[i] - 1 ), 0.5);
            }
            else
                calib[i].error = 0.0;

        }
    }
}

void AppGUI::CreateWindow()
{
    std::string arcName;
    std::map<std::string, std::vector<double> > params;
    ReadConfig(params, arcName);

    ArchiveWrapper arcTest(arcName, -1);
    if( !arcTest.Valid() )
        arcName.clear();
    
    // main frame
    TGMainFrame *fTotallyMainFrame = new TGMainFrame(gClient->GetRoot(),10,10, kMainFrame | kVerticalFrame);
    fTotallyMainFrame->SetWindowName("Tomson");
    fTotallyMainFrame->SetLayoutBroken(kTRUE);

    m_mainCompositeFrame = new TGCompositeFrame(fTotallyMainFrame,1090,700,kVerticalFrame);
    m_mainCompositeFrame->SetLayoutBroken(kTRUE);

    // ???? ???????? ?????? / high level tabs
    TGTab *fMainTabs = new TGTab(m_mainCompositeFrame,1090,700);

    // ??????? ???? ???????? ?????? / create high level tabs
    m_measTab.Create( fMainTabs, this, arcName );

    CreateDataTab( fMainTabs, arcName );

    // ????????????? ???????? ??? ???????? ?????? / set active high level tab
    fMainTabs->SetTab(0);

    m_mainCompositeFrame->AddFrame(fMainTabs, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));
    fMainTabs->MoveResize(8,8,1080,690);

    fTotallyMainFrame->AddFrame(m_mainCompositeFrame, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY));

    fTotallyMainFrame->MapSubwindows();

    fTotallyMainFrame->Resize(fTotallyMainFrame->GetDefaultSize());
    fTotallyMainFrame->MapWindow();
    fTotallyMainFrame->MoveResize(8, 8, 1100,700);

    ConfigureTomsonScattering( params );
    m_visWrapper = new VisualizationWrapper();
}

void AppGUI::ConfigureTomsonScattering( std::map<std::string, std::vector<double> > &params )
{
        bool timingsSet = (params.find("Signal start time") != params.end()) && (params.find("Signal end time") != params.end())
        && (params.find("Signal 12 start time") != params.end()) && (params.find("Signal 12 end time") != params.end());

    if( timingsSet )
    {
        double start = params["Signal start time"].front();
        double end = params["Signal end time"].front();
        double start12 = params["Signal 12 start time"].front();
        double end12 = params["Signal 12 end time"].front();
        m_scatteringMethod.SetSignalTimes( start, end, start12, end12);
    }

    bool offsetsSet = (params.find("Before maximum interval start") != params.end()) && (params.find("Before maximum interval end") != params.end())
        && (params.find("After maximum interval start") != params.end()) && (params.find("After maximum interval end") != params.end());
    if( offsetsSet )
    {
        double beforeMaxStart = params["Before maximum interval start"].front();
        double beforeMaxEnd = params["Before maximum interval end"].front();
        double afterMaxStart = params["After maximum interval start"].front();
        double afterMaxEnd = params["After maximum interval end"].front();
        m_scatteringMethod.SetOffsets( beforeMaxStart, beforeMaxEnd, afterMaxStart, afterMaxEnd);
    }

    if ( params.find("Peak calibration chanel") != params.end() )
    {
        m_scatteringMethod.SetPeakCalibChanel( int(params["Peak calibration chanel"].front()) - 1 );
    }

    bool secPeakTimingSet = (params.find("Second peak start time") != params.end()) && (params.find("Second peak end time") != params.end());
    if( secPeakTimingSet )
    {
        double start = params["Second peak start time"].front();
        double end = params["Second peak end time"].front();

        m_scatteringMethod.SetSecondPeakTimes(start, end);
    }

    bool secPeakOffsetsSet = (params.find("Before second peak interval start") != params.end()) && (params.find("Before second peak interval end") != params.end())
        && (params.find("After second peak interval start") != params.end()) && (params.find("After second peak interval end") != params.end());

    if( secPeakOffsetsSet )
    {
        double beforeSecPeakStart = params["Before second peak interval start"].front();
        double beforeSecPeakEnd = params["Before second peak interval end"].front();
        double afterSecPeakStart = params["After second peak interval start"].front();
        double afterSecPeakEnd = params["After second peak interval end"].front();

        m_scatteringMethod.SetSecondPeakOffsets( beforeSecPeakStart, beforeSecPeakEnd, afterSecPeakStart, afterSecPeakEnd);
    }

    if( params.find("Chanel number") != params.end() )
        m_scatteringMethod.SetChanelNum( params["Chanel number"].front() );

    if( params.find("delta") != params.end() )
        m_scatteringMethod.SetDelta(params["delta"]);

    if( ( params.find("Time sync first peak offset") != params.end() ) && ( params.find("Time sync second peak offset") != params.end() ) )
    {
        m_scatteringMethod.SetTimeSyncSignalOffset( params["Time sync first peak offset"].front() );
        m_scatteringMethod.SetTimeSyncSecondPeakOffset( params["Time sync second peak offset"].front() );
    }

    if( params.find("ADT bit count") != params.end() )
        m_scatteringMethod.SetADTBitCount( params["ADT bit count"].front() );
}

void AppGUI::CreateDataTab( TGTab * fMainTabs, std::string name )
{
    // container of "Tab2"
   TGCompositeFrame *fDataTabFrame;
   fDataTabFrame = fMainTabs->AddTab("Data Tab");
   fDataTabFrame->SetLayoutManager(new TGVerticalLayout(fDataTabFrame));
   fDataTabFrame->SetLayoutBroken(kTRUE);

   // tab widget
   TGTab *fDataTabCalibTab = new TGTab(fDataTabFrame,936,650);
   
   m_calibTab.Create( fDataTabCalibTab, this, name );
   m_configTab.Create( fDataTabCalibTab, this );

   fDataTabCalibTab->SetTab(0);

   fDataTabFrame->AddFrame(fDataTabCalibTab, new TGLayoutHints(kLHintsLeft | kLHintsTop,2,2,2,2));
   fDataTabCalibTab->MoveResize(8,8,1060,650);
}

void AppGUI::BrowseArc()
{
    TGFileInfo fi;
    fi.fMultipleSelection = false;
    new TGFileDialog(NULL, m_mainCompositeFrame, kFDOpen, &fi);
    if( fi.fFilename != NULL )
    {
        m_measTab.SetArcName(fi.fFilename);
        m_calibTab.SetArcName(fi.fFilename);
    }
}
